<?php
namespace app\common\controller;

class WxPayService {
    private $config;
    
    public function __construct() {
        $this->config = [
            'appid' => 'wx0e2a450b8c9ddb8c', // 微信开放平台APPID
            'mch_id' => '1647959165', // 微信支付商户号
            'key' => '89Bn7Bk6PVInaroAYMNYE5GoXeARav1U', // 微信支付API密钥
            'notify_url' => 'https://travel.chengmeiti.cn/api/index/charge_notify', // 支付结果通知地址
        ];
    }
    
    /**
     * 生成随机字符串
     */
    private function generateNonceStr($length = 32) {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
    
    /**
     * 生成签名
     */
    private function makeSign($params) {
        ksort($params);
        $string = '';
        foreach ($params as $k => $v) {
            if($k != 'sign' && $v != '' && !is_array($v)) {
                $string .= $k . '=' . $v . '&';
            }
        }
        $string = trim($string, '&');
        $string .= '&key=' . $this->config['key'];
        return strtoupper(md5($string));
    }
    
    /**
     * 统一下单
     */
    public function unifiedOrder($orderParams) {
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        
        $params = [
            'appid'            => $this->config['appid'],
            'mch_id'           => $this->config['mch_id'],
            'nonce_str'        => $this->generateNonceStr(),
            'body'             => $orderParams['body'] ?? '商品支付',
            'out_trade_no'     => $orderParams['out_trade_no'],
            'total_fee'       => $orderParams['total_fee'], // 单位:分
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
            'notify_url'       => $this->config['notify_url'],
            'trade_type'       => 'APP',
        ];
        
        $params['sign'] = $this->makeSign($params);
        
        $xml = $this->arrayToXml($params);
        $response = $this->postXmlCurl($xml, $url);
        $result = $this->xmlToArray($response);
        
        if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
            return $this->getAppParams($result['prepay_id']);
        }
        
        throw new \Exception($result['return_msg'] ?? '微信支付下单失败');
    }
    
    /**
     * 生成APP支付参数
     */
    private function getAppParams($prepayId) {
        $params = [
            'appid'     => $this->config['appid'],
            'partnerid' => $this->config['mch_id'],
            'prepayid'  => $prepayId,
            'package'  => 'Sign=WXPay',
            'noncestr'  => $this->generateNonceStr(),
            'timestamp' => (string)time(),
        ];
        
        $params['sign'] = $this->makeSign($params);
        return $params;
    }
    
    /**
     * 数组转XML
     */
    private function arrayToXml($arr) {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<".$key.">".$val."</".$key.">";
            } else {
                $xml .= "<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }
    
    /**
     * XML转数组
     */
    private function xmlToArray($xml) {
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }
    
    /**
     * 发送HTTP请求
     */
    private function postXmlCurl($xml, $url, $timeout = 30) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        
        $response = curl_exec($ch);
        if ($response === false) {
            throw new \Exception('curl错误: ' . curl_error($ch));
        }
        curl_close($ch);
        return $response;
    }
}